package spring.cloud.tasks.client.item_task_processor;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

@Slf4j
public class TaskProcessingThreadContext<TaskDataId> {

    @Setter
    @Getter
    Task task;
    @Setter
    @Getter
    protected volatile spring.cloud.tasks.client.item_task_processor.TaskProcessor taskProcessor;
    @Setter
    @Getter
    protected volatile spring.cloud.tasks.client.item_task_processor.api.TaskProcessor $taskProcessor$;
    @Setter
    @Getter
    volatile boolean multipleTaskDataProcessor = false;
    //******************************我是优雅的分割线******************************
    @Setter
    @Getter
    volatile List<Thread> threadList = Collections.synchronizedList(new ArrayList<Thread>());
    @Setter
    @Getter
    protected volatile List<TaskDataId> taskDataIdList = Collections.synchronizedList(new ArrayList<TaskDataId>());
    @Setter
    @Getter
    protected volatile Semaphore getTaskDatasSemaphore = new Semaphore(1, true);//控制1个共享资源的使用，使用先进先出的公平模式进行共享;公平模式的信号量，先来的先获得信号量
    @Setter
    @Getter
    volatile boolean isSleeping = false;
    @Setter
    @Getter
    volatile boolean destroy = false;
    @Setter
    @Getter
    volatile boolean needDestroyTaskDatas = false;
    @Setter
    @Getter
    volatile Statistics statistics;

    public TaskProcessingThreadContext(//
                                       TaskProcessor taskProcessor,//
                                       spring.cloud.tasks.client.item_task_processor.api.TaskProcessor $taskProcessor$,//
                                       Statistics statistics) throws Exception//
    {
        this.task = taskProcessor.getTask();
        this.taskProcessor = taskProcessor;
        this.$taskProcessor$ = $taskProcessor$;
        if (this.$taskProcessor$ instanceof spring.cloud.tasks.client.item_task_processor.api.single_task_data.TaskProcessor) {
            task.setMaxExecuteSize(1);//自动修正
            multipleTaskDataProcessor = false;
        } else {
            multipleTaskDataProcessor = true;
        }
        this.statistics = statistics;
        //
        for (int i = 0; i < taskProcessor.getThreadCountPerTaskProcessor(); i++) {
            this.newThread(i);
        }
    }

    private void newThread(int index) {
        String threadName = this.taskProcessor.getTaskProcessorInfo().getProcessorId() + "-" + index;//
        TaskProcessingThread thread = new TaskProcessingThread(this);//共享信息
        thread.setName(threadName);
        thread.setDaemon(true);
        thread.start();
        threadList.add(thread);
    }

    /**
     * 获取任务
     *
     * @return 任务数量
     */
    protected int getTaskDataIdsFromDb() {

        try {
            this.isSleeping = true;
            TimeUnit.SECONDS.sleep(1);
            this.isSleeping = false;
            //
            List<String> taskItemList = this.taskProcessor.getTaskItemAndParameterListUntilTaskDataListIsAllFinshed();
            if (taskItemList.size() > 0) {
                List<TaskDataId> taskDataIdList = this.$taskProcessor$.getTaskDataList(taskItemList, this.taskProcessor.getTaskItemListSizeInZooKeeper(), task.getTaskParameter(), task.getFetchSize()
                );//
                taskProcessor.getTaskProcessorInfo().setLastFetchDataTime(new Date(taskProcessor.zooKeeperItemTaskDataManager.zooKeeperCurrentTimeMillis()));
                if (taskDataIdList != null) {
                    this.taskDataIdList.addAll(taskDataIdList);
                }
            } else {
            }
            addTaskFetchNum(taskDataIdList.size());
            return this.taskDataIdList.size();
        } catch (Throwable ex) {
            log.error("Get tasks error.", ex);
        }
        return 0;
    }


    public synchronized Object getTaskDataId() {
        if (this.taskDataIdList.size() == 0) {
            return null;
        } else {
            return this.taskDataIdList.remove(0);//第一个
        }
    }

    public synchronized Object[] getTaskDataIds() {
        if (this.taskDataIdList.size() == 0) {
            return null;
        }
        int minSizeBetweenTaskListSizeAndMaxExecuteNumber = taskDataIdList.size() > task.getMaxExecuteSize() ? task.getMaxExecuteSize() : taskDataIdList.size();
        Object[] taskDataIdList = null;
        if (!(minSizeBetweenTaskListSizeAndMaxExecuteNumber > 0)) {
            return taskDataIdList;
        } else {
            taskDataIdList = (Object[]) Array.newInstance(this.taskDataIdList.get(0).getClass(), minSizeBetweenTaskListSizeAndMaxExecuteNumber);
            for (int i = 0; i < minSizeBetweenTaskListSizeAndMaxExecuteNumber; i++) {
                taskDataIdList[i] = this.taskDataIdList.remove(0);//第一个
            }
            return taskDataIdList;
        }

    }

    public boolean noTaskDatas() {
        return this.taskDataIdList.size() == 0;
    }

    public void destroy() throws Exception {
        this.destroy = true;
        for (Thread thread : threadList) {
            try {
                ((TaskProcessingThread) thread).destroy = true;
            } catch (Throwable e) {
                log.error("", e);
            }
        }
        this.threadList.clear();
        //先进行清理标志位处理。再进行任务数据信息清理。
        clearTaskDataIdList();
    }

    public void needDestroyTaskDatas() {
        this.needDestroyTaskDatas = true;
        for (Thread thread : threadList) {
            try {
                ((TaskProcessingThread) thread).needDestroyTaskDatas = true;
            } catch (Throwable e) {
                log.error("", e);
            }
        }
        //先进行清理标志位处理。再进行任务数据信息清理。
        clearTaskDataIdList();
    }

    public void clearTaskDataIdList() {
        this.taskDataIdList.clear();
    }

    //
    public void addTaskFetchNum(long num) {

        this.statistics.addFetchDataCount(1);
        this.statistics.addFetchTaskFetchNum(num);
    }

    public void addTaskSuccessNum(long num, long spendTime) {
        this.statistics.addTaskSuccessNum(num);
        this.statistics.addSpendTime(spendTime);
    }

    public void addTaskFailNum(long num, long spendTime) {
        this.statistics.addTaskFailNum(num);
        this.statistics.addSpendTime(spendTime);
    }
}
